package cn.dengta.webapp.user.entity;

import java.util.*;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

import cn.dengta.common.context.I18N;
import cn.dengta.common.model.*;
import cn.dengta.common.util.Common;
import cn.dengta.common.util.SpringUtil;
import cn.dengta.common.web.Client;
import cn.dengta.context.model.*;
import cn.dengta.context.web.RequestContextImpl;
import cn.dengta.webapp.base.entity.BizEntity;
import cn.dengta.webapp.user.cache.UserSummary;
import cn.dengta.webapp.user.dto.UserMenuPermissionDTO;
import cn.dengta.webapp.user.model.*;
import cn.dengta.webapp.user.service.RemoteUserService;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import me.codeplayer.util.*;

@Getter
@Setter
@Accessors(chain = true)
@JSONType(ignores = { "username_", "admin", "valid", "avatared", "avataring", "destroyed",
		"realNamed", "realName_", "certNo_", "emailed", "frozen", "gender_", "phoned", "zodiac",
		"enabled", "stateVal", "present", "state_", "toggle", "toggle_",
})
@TableName("dt_user")
public class User extends MemberContent implements UserSketch {

	/** APP 审核账号用户ID集合 */
	public static final Set<Long> APP_VERIFY_USER_IDS = Set.of(
			7L /* Google 上架审核账号 testhelloword668@163.com */
			, 68L /* Apple 上架审核账号 dzttkj@gmail.com */
			, 1421793L /** flala苹果审核号 */
			, 119164L /* Apple 上架审核专用手机号码 852-12349876 */
			, 528529L /* Android 国内上架账号 86-15817261106 */
			, 1041498L /* Facebook 审核账号 sshi05088@gmail.com */
			, 300000011L /** 聊郁 IOS 审核账号 */
			, 500000003L /** Contala IOS 审核账号 contalaiosuser@gmail.com  */
			, 500000018L /** Contala Google 审核账号 boclk@tianmi.net */
			, 400000008L /** 福诉审核号 */
			, 600000006L /** holaMet google审核号， holamet@163.com */
	);

	/** 供苹果用户进行审核的用户ID */
	public static final long appleReviewUserId = 68L;
	/** 用户在线状态的最大活跃延迟毫秒数，即最近活跃时间在 多少ms 以内的，就视为在线 */
	public static final long onlineMaxDelayMs = 121_000L;

	/** 默认出生日期 */
	public static final Date DEFAULT_BIRTHDAY = new EasyDate(1990, 1, 1).toDate();

	public static final Integer DEFAULT_SORT = 0;

	/** 标识：是否设置过语言 */
	public static final long BIZ_FLAG_CHANGE_LANGUAGE = 1L;
	/** 标识：是否存在头像审核 */
	public static final long BIZ_FLAG_VERIFY_AVATARING = 1L << 2;
	/** 标识：是否可登录后台（用于登录可控）还需要区分对应类型 */
	public static final long BIZ_FLAG_CAN_LOGIN_BACKSTAGE = 1L << 3;
	/** 标识：风险用户 */
	public static final long BIZ_FLAG_HAS_RISK = 1L << 4;
	/** 标识：审核复制标记 */
	public static final long BIZ_FLAG_AUDIT_COPY_MARK = 1L << 5;
	/** 标识：是否可直播 */
	public static final long BIZ_FLAG_CAN_LIVE = 1L << 6;
	/** 标识：是否来自大陆 */
	public static final long BIZ_FLAG_FROM_CHINA = 1L << 7;
	/** 标识：是否存在昵称审核 */
	public static final long BIZ_FLAG_NICKNAME_VERIFYING = 1L << 8;
	/** 标识：是否倾向支付 */
	public static final long BIZ_FLAG_BIASED_PAY = 1L << 9;
	/** 标识：是否标记钻石收费 */
	public static final long BIZ_FLAG_MARK_PAY_COIN = 1L << 10;

	public static final long BIZ_FLAG_CLOUD_ACCOUNT_SIGN = 1L << 11;
	/** 是否自动加入白名单 */
	public static final long BIZ_AUTO_REC_LEVEL = 1L << 12;

	/** IM心跳过期时间 */
	public final static int HEARTBEAT_INTERVAL_SECOND = 61;
	/** 女用户活跃时间的临界点，3分钟 */
	public final static long HEARTBEAT_INTERVAL_MILLISECOND = HEARTBEAT_INTERVAL_SECOND * 1000L;
	/** 男用户活跃时间的临界点，10分钟 */
	public final static long MAN_HEARTBEAT_INTERVAL_MILLISECOND = HEARTBEAT_INTERVAL_SECOND * 2 * 1000L;

	/** 用户扩展信息关联ID */
	protected Long userInfoId;
	/** 用户类型 */
	protected UserType type;
	/** 电视墙是否匿名：0=否；1=是 */
	protected Integer tvAnonymous;
	/** VIP到期时间 */
	protected Date vipExpireTime;
	/** VIP充值时间 */
	protected Date vipRechargeTime;
	/** VIP配置id */
	protected Long vipConfigId;
	/** 高级筛选：0=随机，1=头像认证 */
	protected Integer advancedFilter;
	/** 性别偏好 */
	protected Integer likeGender;
	/** 语言偏好（多个逗号隔开） */
	protected String likeLanguage;
	/** 标签偏好（多个逗号隔开） */
	protected String likeTag;
	/** 喜欢专属类型（多个逗号隔开） */
	protected String likeGenre;
	/** 额外搭讪展示到期时间 */
	protected Date extraRecUpExpireTime;
	/** 语言 */
	protected Language language;
	/** 资料已补充标识 */
	protected long infoSupplyFlag;
	/** 注册端 */
	protected Client client;
	/** 云信token */
	protected String imToken;
	/** 推广员id */
	protected Long agentId;
	/** 推广员信息（多个逗号隔开）（推广员级别，推广员类型，推广员范围） */
	protected String agentInfo;
	/** 加入推广员时间 */
	protected Date agentJoinTime;
	/** 渠道ID */
	protected Long channelId;
	/** 最后活跃时间 */
	protected Long lastActive;
	/** 推荐等级 */
	protected RecommendLevel recLevel;
	/** 国家 */
	protected String country;
	/** 国家偏好 */
	protected String likeCountry;
	/** 排序 */
	protected Integer sort;
	/** 白名单权重 */
	protected Integer whiteSort;
	/** 注册国家 */
	protected String regCountry;
	/** 投放类型 */
	protected String appType;
	/** 第三方投放渠道ID */
	protected String appId;
	/** 马甲包 */
	protected WesKit wesKit;
	/** 地区ID 0=未知 */
	protected Long areaId;
	@TableField(exist = false)
	protected UserInfo userInfo;
	/** 自动登录是否为注册 */
	@TableField(exist = false)
	public transient Boolean isRegister;
	/** 拉黑时间 */
	protected Date blackTime;
	/** 邀请关系等级 */
	protected InviteLevel inviteLevel;
	/** 真人认证：1=已认证；0=未认证 */
	protected Integer realPeople;
	/** 退出推荐列表时间 */
	protected Date outRecListTime;
	/** 退出在线列表时间 */
	protected Date outOnlineListTime;
	/** 应用市场渠道包名 */
	private String channel;
	/** 登录版本号 */
	private String version;
	/** 当前客户端(登录和心跳更新) */
	private Client currClient;

	/** 青少年模式：0=关；1=开； */
	private Integer teenFlag;
	/** 青少年模式密码 */
	private String teenPassword;

	@Override
	public User init(BizEntity input, Date now) {
		super.init(input, now);
		User bean = (User) input;
		tvAnonymous = X.expectNotNull(bean.tvAnonymous, tvAnonymous, 0); // 默认不匿名
		this.advancedFilter = X.expectNotNull(bean.advancedFilter, advancedFilter, 0);
		vipExpireTime = X.expectNotNull(vipExpireTime, bean.vipExpireTime);
		likeLanguage = X.expectNotNull(likeLanguage, "");
		lastActive = X.expectNotNull(lastActive, 0L);
		sort = X.expectNotNull(sort, DEFAULT_SORT);
		inviteLevel = X.expectNotNull(inviteLevel, InviteLevel.UNKNOWN);
		outRecListTime = X.expectNotNull(outRecListTime, new Date());
		outOnlineListTime = X.expectNotNull(outOnlineListTime, new Date());
		this.teenFlag = 0;
		super.initTime(now);
		super.init(Status.YES.value, State.PUBLIC);
		return this;
	}

	public Integer calcLikeGender() {
		if (likeGender != null) {
			return likeGender;
		}
		return otherGender().value;
	}

	/** 是否实名认证 */
	public boolean hasRealName() {
		return Status.asResult(this.realNameStatus);
	}

	/** 是否真人认证 */
	public boolean hasRealPeople() {
		return Status.asResult(this.realPeople);
	}

	public Gender otherGender() {
		return Gender.getOrDefault(gender).other();
	}

	@JSONField(serialize = false)
	public Integer getAge() {
		return age();
	}

	/**
	 * 是否投放渠道来源
	 */
	public boolean hasChannel() {
		return Cmp.gt(channelId, 0);
	}

	/** 是否VIP */
	@Override
	public boolean vip() {
		return validVipTime();
	}

	/** 是否是单向付费（对方付费） */
	@Override
	public boolean biasedPay() {
		return hasFlag(BIZ_FLAG_BIASED_PAY);
	}

	/** 是否可直播 */
	@Override
	public boolean canLive() {
		return hasFlag(BIZ_FLAG_CAN_LIVE);
	}

	/** 是否新人【注册三天内】 */
	public boolean forNewcomer() {
		return new EasyDate(addTime).addDay(2).endOf(Calendar.DAY_OF_MONTH).getTime() > System.currentTimeMillis();
	}

	/** 电视墙是否匿名 **/
	public boolean anonymous() {
		return Status.asResult(tvAnonymous);
	}

	/** VIP时间是否有效 */
	public boolean validVipTime() {
		return vipExpireTime != null && vipExpireTime.getTime() > System.currentTimeMillis();
	}

	/**
	 * 计算 VIP 结束时间
	 */
	public Date calcVipTime(@Nullable Date oldExpireTime, long now, int days, Long vipConfigId) {
		final EasyDate d = new EasyDate(now);
		if (oldExpireTime != null && oldExpireTime.getTime() > now && vipConfigId != null) {
			d.setDate(oldExpireTime);
		}
		return d.addDay(days).endOf(Calendar.DAY_OF_MONTH).toDate();
	}

	/**
	 * 判断VIP充值时间
	 */
	public Date vipRechargeTime(long now) {
		if (vip()) {
			return X.expectNotNull(vipRechargeTime, vipExpireTime);
		}
		return new Date(now);
	}

	/** 是否已通过头像认证 */
	public boolean isAvatared() {
		return VerifyStatus.REVIEW_TRUE.eq(avatarStatus);
	}

	/** 头像认证中 */
	public boolean isAvataring() {
		return VerifyStatus.VERIFY_TRUE.eq(avatarStatus);
	}

	/** 存在审核中头像 */
	public boolean avatarVerifying() {
		return hasFlag(User.BIZ_FLAG_VERIFY_AVATARING);
	}

	public boolean hasAgent() {
		return X.isValid(agentId);
	}

	public boolean existAgent() {
		return UserType.AGENT == type || hasAgent();
	}

	/** 需要强制设置头像 */
	@Override
	public boolean needSetAvatar() {
		return Gender.FEMALE == gender && !avatarVerifying() && !hasChangeAvatar();
	}

	/** 需要强制设置推广员 */
	@Override
	public boolean needSetAgent() {
		return Gender.FEMALE == gender && isMember() && !hasAgent();
	}

	/** 是否修改过头像 */
	public boolean hasChangeAvatar() {
		return StringUtil.notEmpty(avatar);
	}

	public boolean needSetLikeLanguage() {
		return StringUtil.isEmpty(likeLanguage);
	}

	public boolean needSetLikeTag() {
		return StringUtil.isEmpty(likeTag);
	}

	public boolean needSetLikeGenre() {
		return StringUtil.isEmpty(likeGenre);
	}

	/** 增加额外展示时长时长 */
	public Date calcExtraRecUpTime(int minute) {
		EasyDate d = new EasyDate();
		Date base = extraRecUpExpireTime != null && extraRecUpExpireTime.getTime() > d.getTime() ? extraRecUpExpireTime : null;
		if (base != null) {
			d.setDate(base);
		}
		return d.addMinute(minute).toDate();
	}

	@Override
	public boolean freeze() {
		return !UserStatus.VALID.eq(this.status) || this.freezeTime != null && this.freezeTime.getTime() > System.currentTimeMillis();
	}

	/** 计算冻结时间 */
	public Date calcFreezeTime(int day, DateUnit unit) {
		return unit.add(freezeTime != null && freezeTime.getTime() > System.currentTimeMillis() ? freezeTime : null, day);
	}

	/** 额外搭讪展示到期时间倒计时 */
	public long extraRecUpExpireTimeDiff() {
		return extraRecUpExpireTime != null ? Math.max(extraRecUpExpireTime.getTime() - System.currentTimeMillis(), 0L) : 0L;
	}

	public boolean forceSetTag() {
		return gender == null;
	}

	public String nationalFlag() {
		return defaultCountry().getNationalFlag();
	}

	public String national() {
		return defaultCountry().getLabel();
	}

	@Override
	public boolean canLoginBackstage() {
		return canLogin() && isAgent() && hasFlag(BIZ_FLAG_CAN_LOGIN_BACKSTAGE);
	}

	@JSONField(serialize = false)
	public Integer getSupplyProgress() {
		return UserInfoSupply.calcScore(this.getWesKit(), infoSupplyFlag);
	}

	/** 是否可查看信息 */
	public boolean canView() {
		return isEnabled() && status > 0;
	}

	/** 是否存在绑定关系 */
	public boolean hasInviteLevel() {
		return inviteLevel != null && inviteLevel.value > InviteLevel.UNKNOWN.value;
	}

	/** 是否存在绑定关系 */
	public boolean hasBindInvite() {
		return hasInvite() && hasInviteLevel();
	}

	public void addInviteUser(User inviteUser) {
		if (inviteUser != null) {
			this.inviteUserId = inviteUser.getId();
			this.inviteLevel = InviteLevel.STRONG;
		}
	}

	public boolean hasInviteUserId() {
		return X.isValid(this.inviteUserId);
	}

	@Override
	public void setId(Long id) {
		this.id = id;
	}

	@Override
	public boolean hasChangeLanguage() {
		return hasFlag(BIZ_FLAG_CHANGE_LANGUAGE);
	}

	public static void checkLogin(UserSummary user) {
		I18N.assertTrue(user.canLogin(), UserI18nKey.USER_AUTO_LOGIN_FROZEN);
	}

	public List<Language> likeLanguages() {
		if (StringUtil.notEmpty(likeLanguage)) {
			return Common.split(likeLanguage, ',', s -> Language.of(Integer.parseInt(s)), true);
		}
		return null;
	}

	public String likeLanguageAbbr() {
		if (StringUtil.notEmpty(likeLanguage)) {
			return Common.splitAsIntList(likeLanguage).stream().map(Language::abbrName).filter(Objects::nonNull).collect(Collectors.joining("/"));
		}
		return null;
	}

	/** 是否推广员账号 */
	@JSONField(serialize = false)
	public boolean isAgent() {
		return type == UserType.AGENT;
	}

	/** 是否普通用户 */
	@JSONField(serialize = false)
	public boolean isMember() {
		return type == UserType.MEMBER;
	}

	public void joinAgent(Long agentId, String agentInfo, Date now) {
		this.agentId = agentId;
		this.agentInfo = StringUtil.toString(agentInfo);
		this.agentJoinTime = now;
	}

	@Override
	public boolean inChina() {
		return Country.CN.eq(regCountry);
	}

	@Override
	public boolean acceptCoinOnly() {
		return getGender() == Gender.FEMALE && markPayCoin();
	}

	public List<Language> likeLanguagesEnum() {
		final List<Language> languages = likeLanguages();
		return languages == null ? Collections.singletonList(language) : languages;
	}

	public boolean riskUser() {
		return hasFlag(BIZ_FLAG_HAS_RISK);
	}

	public boolean markPayCoin() {
		return hasFlag(BIZ_FLAG_MARK_PAY_COIN);
	}

	public boolean eqPhone(String phone) {
		return PhoneArea.eqPhone(this.phone, phone);
	}

	//	public void setTeenPassword(String teenPassword) {
	//		this.teenPassword = encryptTeenPassword(teenPassword, addTime);
	//	}

	private String encryptTeenPassword(String password, Date addTime) {
		return UserFieldAutoGenerator.encryptPassword(password, addTime);
	}

	public boolean checkTeenPassword(String inputPassword) {
		return StringUtil.notEmpty(inputPassword) && teenPassword.equals(encryptTeenPassword(inputPassword, addTime));
	}

	@Override
	@JSONField(serialize = false)
	public Set<String> getPermissions() {
		if (permissions == null) {
			initMenuAndPermission(false);
		}
		return permissions;
	}

	public void initMenuAndPermission(boolean flush) {
		if (isAgent() && (flush || permissions == null)) {
			RemoteUserService remoteUserService = SpringUtil.getBean("remoteUserService");
			UserMenuPermissionDTO dto = null;
			if (!flush) {
				dto = remoteUserService.getPermissions(getId());
			}
			if (flush || !X.isValid(permissions)) {
				if (dto == null) {
					dto = remoteUserService.findPermissionCodesByUserId(getId());
					if (dto == null) {
						dto = new UserMenuPermissionDTO().init();
					}
					remoteUserService.putPermissions(getId(), dto);
				}
				this.permissions = dto.getPermissionSet();
				this.routeCodes = dto.getRouteCodeSet();
			}
		}
	}

	/** 基础：ID、昵称、头像 */
	public static Map<String, Object> asEmbed(final UserSketch user) {
		return asEmbed(user, t -> CollectionUtil.ofHashMap(
				"id", Member.encode(t.getId()),
				"uId", t.getId(),
				"name", t.getNickname(),
				"avatar", t.avatarOrDefault(),
				"needPay", t.acceptCoinOnly()
		));
	}

	/** 扩展：VIP */
	public static Map<String, Object> asEmbedWithVIP(final UserSummary user) {
		return X.map(user, t -> CollectionUtil.ofHashMap(
				"id", Member.encode(t.getId()),
				"name", t.getNickname(),
				"avatar", t.avatarOrDefault(),
				"vip", t.vip(),
				"needPay", t.acceptCoinOnly()
		));
	}

	/** 扩展：VIP、性别 */
	public static Map<String, Object> asEmbedWithGender(final UserSketch user) {
		return asEmbed(user, t -> CollectionUtil.ofHashMap(
				"id", Member.encode(t.getId()),
				"name", t.getNickname(),
				"avatar", t.avatarOrDefault(),
				"vip", t.vip(),
				"gender", X.map(t.getGender(), Gender::getValue),
				"gender_", t.gender_(),
				"needPay", t.acceptCoinOnly()
		));
	}

	/** 扩展：VIP、性别、风险标识 */
	public static Map<String, Object> asEmbedWithRisk(User user) {
		return asEmbed(user, t -> CollectionUtil.ofHashMap(
				"id", t.encodedId(),
				"name", t.getNickname(),
				"avatar", t.avatarOrDefault(),
				"vip", t.vip(),
				"gender_", t.gender_(),
				"fontColor", t.riskUser() ? "#F43131" : null
		));
	}

	/** 扩展：年龄、性别、星座 */
	public static Map<String, Object> asEmbedWithInfo(UserSketch user) {
		return asEmbed(user, t -> CollectionUtil.ofHashMap(
				"id", Member.encode(t.getId()),
				"name", t.getNickname(),
				"avatar", t.avatarOrDefault(),
				"age", t.age(),
				"gender", X.map(t.getGender(), Gender::getValue),
				"gender_", t.gender_(),
				"constellation", t.getConstellation(),
				"needPay", t.acceptCoinOnly()
		));
	}

	/** 只有：国/区旗、国家/地区 */
	public static Map<String, Object> asEmbedCountry(User user) {
		return asEmbed(user, t -> CollectionUtil.ofHashMap(
				"nationalFlag", t.nationalFlag(),
				"national", t.national(),
				"country", t.defaultCountry().getValue()
		));
	}

	/** 扩展：VIP、年龄、性别、星座 */
	public static Map<String, Object> asEmbedWithAge(final UserSummary user, String nickname) {
		WesKit wesKit = RequestContextImpl.get().getWesKit();
		return X.map(user, t -> {
			final Country country = Country.of(t.getCountry());
			HashMap<String, Object> hashMap = CollectionUtil.ofHashMap(
					"id", Member.encode(t.getId()),
					"uId", t.getId(),
					"name", StringUtil.notEmpty(nickname) ? nickname : t.getNickname(),
					"avatar", t.avatarOrDefault(),
					"vip", t.vip(),
					"gender", X.map(t.getGender(), Gender::getValue),
					"gender_", t.gender_(),
					"age", t.age(),
					"isAvatared", t.isAvatared(),
					"realPeople", t.hasRealPeople(),
					"constellation", t.getConstellation(),
					"needPay", t.acceptCoinOnly()
			);
			if (country != null) {
				hashMap.put("country", t.getCountry());
				hashMap.put("country_", X.map(country, c -> c.getLabel(t.getLanguage())));
				hashMap.put("countryIcon", X.map(country, Country::getNationalFlag));
			}
			return hashMap;
		});
	}

	public static void extendInfo(Map<String, Object> map, final UserInfo userInfo) {
		if (map == null) {
			return;
		}
		X.use(userInfo, t -> map.put("slogan", t.getSlogan()));
	}

	public static final String REC_LEVEL = "rec_level";
	public static final String SORT = "sort";
	public static final String INFO_SUPPLY_FLAG = "info_supply_flag";
	public static final String VIP_EXPIRE_TIME = "vip_expire_time";
	public static final String COUNTRY = "country";
	public static final String WHITE_SORT = "white_sort";
	public static final String REG_COUNTRY = "reg_country";
	public static final String LANGUAGE = "language";
	public static final String CHANNEL_ID = "channel_id";
	public static final String USER_INFO_ID = "user_info_id";
	public static final String LAST_ACTIVE = "last_active";
	public static final String AGENT_ID = "agent_id";
	public static final String ADVANCED_FILTER = "advanced_filter";
	public static final String LIKE_GENDER = "like_gender";
	public static final String BLACK_TIME = "black_time";
	public static final String WES_KIT = "wes_kit";
	public static final String OUT_REC_LIST_TIME = "out_rec_list_time";
	public static final String OUT_ONLINE_LIST_TIME = "out_online_list_time";
	public static final String CHANNEL = "channel";
	public static final String VERSION = "version";
	public static final String TEEN_FLAG = "teen_flag";
	public static final String TEEN_PASSWORD = "teen_password";
	public static final String REAL_PEOPLE = "real_people";

}
